import { useRef } from 'react'
import type { Plugin, Result } from 'ahooks/lib/useRequest/src/types'

type TStateKeys = 'loading' | 'data' | 'error' | 'params'

/** useRequest 插件：将 useRequest 结果同步更新到 store */
export const useRequestSyncStorePlugin = <TData = any, TParams extends any[] = any>({
  onMaybeChange,
}: {
  /**
   * 不一定发生变更，外部最好做个比较
   * - 触发时机：
   *   - init: 全部(包括方法)
   *   - onBefore: { loading: true, params }
   *   - onSuccess: { loading: false, data, error: undefined, params }
   *   - onError: { loading: false, data: undefined, error, params }
   *   - onFinally: { loading: false, data, error, params }
   *   - onCancel: { loading: false }
   *   - onMutate: { data }
   */
  onMaybeChange: (request: Result<TData, TParams>) => void
}): Plugin<TData, TParams> => {
  const objRef = useRef<{
    readonly emptyArr: []
    methods: null | Omit<Result<TData, TParams>, TStateKeys>
  }>({
    emptyArr: [],
    methods: null,
  })

  return (fetchInstance, options) => {
    if (!objRef.current.methods) {
      objRef.current.methods = {
        cancel: fetchInstance.cancel.bind(fetchInstance),
        refresh: fetchInstance.refresh.bind(fetchInstance),
        refreshAsync: fetchInstance.refreshAsync.bind(fetchInstance),
        run: fetchInstance.run.bind(fetchInstance),
        runAsync: fetchInstance.runAsync.bind(fetchInstance),
        mutate: fetchInstance.mutate.bind(fetchInstance),
      }
      onMaybeChange({
        loading: false,
        data: undefined,
        error: undefined,
        params: options.defaultParams || objRef.current.emptyArr,
        ...objRef.current.methods,
      })
    }

    const mergeResult = (state: Partial<Pick<Result<TData, TParams>, TStateKeys>>): Result<TData, TParams> => ({
      loading: fetchInstance.state.loading,
      data: fetchInstance.state.data,
      error: fetchInstance.state.error,
      params: fetchInstance.state.params || objRef.current.emptyArr,
      ...state,
      ...objRef.current.methods!,
    })

    return {
      onBefore: params => {
        onMaybeChange(mergeResult({ loading: true, params }))
      },
      onSuccess: (data, params) => {
        onMaybeChange(mergeResult({ loading: false, data, error: undefined, params }))
      },
      onError: (error, params) => {
        onMaybeChange(mergeResult({ loading: false, data: undefined, error, params }))
      },
      onFinally: (params, data, error) => {
        onMaybeChange(mergeResult({ loading: false, data, error, params }))
      },
      onCancel: () => {
        onMaybeChange(mergeResult({ loading: false }))
      },
      onMutate: data => {
        onMaybeChange(mergeResult({ data }))
      },
    }
  }
}
